﻿using DetectingKinectSencor._40_Tools;
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DetectingKinectSencor._03_Slides
{
    class SlidesManager
    {
        private Stopwatch counterTillSlideChangeDetected;
        private Stopwatch counterTillVideoDetected;
        private Stopwatch counterTimePerSlide;
        private Stopwatch counterTimePerSlide2;
        private Stopwatch counterTillSaveSlideChange;
        private Stopwatch testcounter;

        private float[] histogramRedCurrent;
        private float[] histogramGreenCurrent;
        private float[] histogramBlueCurrent;


        private byte[] previousColorPixels = null;

        // Database
        private SQLiteConnection databaseConnection;
        private SQLiteCommand sqliteCommandSlideChange;
        private SQLiteCommand sqliteCommandVideoDetected;
        private SQLiteCommand sqliteCommandImageDetected;


        private DateTime videoDetectedTimePoint;

        private bool videoDetected = false;

        private bool slideChangeDetected;
        private DateTime slideChangeDetectedTimePoint;

        public DateTime startedCaptureTime { get; set; }

        public int loopCalculatePixelChangeEnd { get; set; }
        public int loopCalculatePixelChangeStart { get; set; }
        public int loopCalculatePixelChangeEndHight { get; set; }

        public bool speakerOnTheRightSide { get; set; }

        private int changedPixelSumedUp;

        private int changedPixelAverage;

        private int rectangleWidth;

        private int counterForSlideChangeColorPixel;
        private int numberOfFramesUsedForSlideChangeDetection = 5;

        private int counterVideoDetected;

        private float totalAmountOfBGRHistogramChangedPixels;
        private int secondsIncrement = 1;

        private float totalAmountOfBGRHistogramChangedPixelsBlue;
        private float totalAmountOfBGRHistogramChangedPixelsGreen;
        private float totalAmountOfBGRHistogramChangedPixelsRed;

        private bool imageDetected;
        private DateTime imageDetectedTimePoint;

        private float oldTotalAmountOfBGRHistogramChangedPixels;
        private float differenceTotalAmountOfBGRHistogramChangedPixels;
        private int counterForVideoDetection;
        private int numberOfFramesForTillVideoDetection = 30;



        public SlidesManager(SQLiteConnection databaseConnection, 
                             int colorFrameWidth, int colorFrameHight)
        {
            this.databaseConnection = databaseConnection;

            // create a new SQL commands:
            sqliteCommandSlideChange = databaseConnection.CreateCommand();
            sqliteCommandVideoDetected = databaseConnection.CreateCommand();
            sqliteCommandImageDetected = databaseConnection.CreateCommand();

            counterTillSlideChangeDetected = new Stopwatch();
            counterTillVideoDetected = new Stopwatch();
            counterTimePerSlide = new Stopwatch();
            counterTimePerSlide2 = new Stopwatch();
            counterTillSaveSlideChange = new Stopwatch();
            testcounter = new Stopwatch();

            loopCalculatePixelChangeStart = 0;
            loopCalculatePixelChangeEnd = colorFrameWidth;

            loopCalculatePixelChangeEndHight = colorFrameHight;

            histogramRedCurrent = new float[256];
            histogramGreenCurrent = new float[256];
            histogramBlueCurrent = new float[256];
        }


        /// <summary>
        /// Starts the threads to track the slides
        /// </summary>
        /// <param name="currentColorPixels"></param>
        /// <param name="pixelDataLength"></param>
        /// <param name="colorFrameWidth"></param>
        /// <param name="colorFrameHight"></param>
        public unsafe void trackingSlides(byte[] currentColorPixels, int pixelDataLength, int colorFrameWidth, int colorFrameHight)
        {
 //           timeBefore = DateTime.Now;

            // Increment the counter for the average calculation
            counterForSlideChangeColorPixel++;

            counterForVideoDetection++;

            if (previousColorPixels == null)
            {
                Console.WriteLine("null");
                previousColorPixels = new byte[pixelDataLength];
                previousColorPixels = currentColorPixels;
            }


            fixed (byte* currentcolorPixelsPointer = currentColorPixels,
                         currentColorPixels2Pointer = currentColorPixels,
                         previousPixelsPointer = previousColorPixels)
            {

                byte* tempCurrentPixelsPointer = currentcolorPixelsPointer;
                byte* tempCurrentColorPixels2 = currentColorPixels2Pointer;
                byte* tempPreviousPixelsPointer = previousPixelsPointer;


                // Color Frame has four bytes per pixel
                try
                {
                    // Loop through the Hight
                    for (int j = 0; j < loopCalculatePixelChangeEndHight; j++)
                    {
                        // If the projection of the slides is on the right half of the width, jump over the left side of the width
                        if (!speakerOnTheRightSide)
                        {
                            // Jump over the first part of the width, *4 because there are always 4 bytes per pixel
                            tempCurrentPixelsPointer += (loopCalculatePixelChangeStart) << 2;
                            tempCurrentColorPixels2 += (loopCalculatePixelChangeStart) << 2;
                            tempPreviousPixelsPointer += (loopCalculatePixelChangeStart) << 2;
                        }

                        // Loop through the Width
                        for (int i = loopCalculatePixelChangeStart; i < loopCalculatePixelChangeEnd; i++)
                        {

                                // Create color histogram
                                histogramBlueCurrent[*tempCurrentPixelsPointer++]++;
                                histogramGreenCurrent[*tempCurrentPixelsPointer++]++;
                                histogramRedCurrent[*tempCurrentPixelsPointer++]++;


                            changedPixelSumedUp += (byte)((
                                        (*tempCurrentColorPixels2 - *tempPreviousPixelsPointer) * (*tempCurrentColorPixels2++ - *tempPreviousPixelsPointer++) // Compare blue pixels
                                        +
                                        (*tempCurrentColorPixels2 - *tempPreviousPixelsPointer) * (*tempCurrentColorPixels2++ - *tempPreviousPixelsPointer++) //Compare green pixels
                                        +
                                        (*tempCurrentColorPixels2 - *tempPreviousPixelsPointer) * (*tempCurrentColorPixels2++ - *tempPreviousPixelsPointer++)  //Compare red pixels
                                        ) / (255 * 3));


                            // Increment the pointers because the fourth byte is always null!
                            tempCurrentPixelsPointer++;
                            tempCurrentColorPixels2++;
                            tempPreviousPixelsPointer++;                            
                        }



                        // If the projection of the slides is on the left half of the width, jump over the right side of the width
                        if (speakerOnTheRightSide)
                        {
                            // Jump over the rest of the width, *4 because there are always 4 bytes per pixel
                            tempCurrentPixelsPointer += (colorFrameWidth - loopCalculatePixelChangeEnd) << 2;
                            tempCurrentColorPixels2 += (colorFrameWidth - loopCalculatePixelChangeEnd) << 2;
                            tempPreviousPixelsPointer += (colorFrameWidth - loopCalculatePixelChangeEnd) << 2;
                        }
                    }

                    // Calculate the width of the rectangle
                    rectangleWidth = loopCalculatePixelChangeEnd - loopCalculatePixelChangeStart;


                }
                catch (Exception ex)
                {
                    Console.WriteLine("Exception slide change detection loop: " + ex.Message);
                }




                // Check after 2 seconds an on new slide, if it contains an image
                if (counterTimePerSlide.Elapsed.Seconds > 2)
                {

                    // when a slide change was detected, investigate the new slide, if it contains an image
                    // Only the middle histogram values are used. Because there is the most change if the slide contains an image
                    // Data is normalized, dependent on the size of the rectangle
                    for (int i = 130; i < 200; i++)
                    {
                        histogramBlueCurrent[i] = histogramBlueCurrent[i] / (loopCalculatePixelChangeEndHight * rectangleWidth);
                        histogramGreenCurrent[i] = histogramGreenCurrent[i] / (loopCalculatePixelChangeEndHight * rectangleWidth);
                        histogramRedCurrent[i] = histogramRedCurrent[i] / (loopCalculatePixelChangeEndHight * rectangleWidth);
                    }


                    // Calculate the entries for the BGR histogram the sum is then checked against a threshold
                    // if the threshold is exceeded than a image is detected
                    for (int i = 130; i < 200; i++)
                    {
                        totalAmountOfBGRHistogramChangedPixelsBlue += histogramBlueCurrent[i];
                        totalAmountOfBGRHistogramChangedPixelsGreen += histogramGreenCurrent[i];
                        totalAmountOfBGRHistogramChangedPixelsRed += histogramRedCurrent[i];
                    }

                    totalAmountOfBGRHistogramChangedPixels = totalAmountOfBGRHistogramChangedPixelsBlue
                                                           + totalAmountOfBGRHistogramChangedPixelsGreen
                                                           + totalAmountOfBGRHistogramChangedPixelsRed;

                    totalAmountOfBGRHistogramChangedPixelsBlue = 0;
                    totalAmountOfBGRHistogramChangedPixelsGreen = 0;
                    totalAmountOfBGRHistogramChangedPixelsRed = 0;

                }

                if (totalAmountOfBGRHistogramChangedPixels > XMLproperties.thresholdImageDetected && !imageDetected)
                {
                    imageDetected = true;
                    imageDetectedTimePoint = DateTime.Now;
                }


                if (counterForVideoDetection >= numberOfFramesForTillVideoDetection)                                                                                                                                                                          
                {
                    // the oldTotalamount is 5 frames old
                    differenceTotalAmountOfBGRHistogramChangedPixels = Math.Abs(oldTotalAmountOfBGRHistogramChangedPixels -
                                                                       totalAmountOfBGRHistogramChangedPixels);


                    // Check if a video was detected
                    if (differenceTotalAmountOfBGRHistogramChangedPixels > XMLproperties.thresholdVideoDetected && !videoDetected)
                    {
                        // Detected that the video is played or slide change
                        videoDetectedTimePoint = DateTime.Now;

                        if (!counterTillVideoDetected.IsRunning)
                            counterTillVideoDetected.Start();

                        if (counterTillVideoDetected.IsRunning && !videoDetected)
                        {
                            // The idea is to check the average every second. In those seconds the average must be above
                            //  the offsetVideoDetected, if this is the case, then is a video
                            if (counterTillVideoDetected.Elapsed.Seconds > secondsIncrement)
                            {
                                secondsIncrement++;

                                //                               Console.WriteLine("changedPixelAverage" + changedPixelAverage);

                                counterVideoDetected++;

                                if (counterVideoDetected >= XMLproperties.requiredAveragesToDetectVideo)
                                {
                                    videoDetected = true;
                                    secondsIncrement = 1;
                                    counterVideoDetected = 0;
                                    counterTillVideoDetected.Reset();
                                }
                            }

                            // If the average did not achive to raise the counter to detect the video by 3. then 
                            // it is assumed that it is not a video and everthing is reset
                            if (counterTillVideoDetected.Elapsed.Seconds > 10)
                            {
                                counterVideoDetected = 0;
                                counterTillVideoDetected.Reset();
                                secondsIncrement = 1;
                            }
                        }
                    }
                    counterForVideoDetection = 0;
                }



                // Create the average for the pixels
                changedPixelAverage = changedPixelSumedUp / counterForSlideChangeColorPixel;




                // If Amount of pixels is bigger than the threshold, than a slide change is detected
                if (changedPixelAverage > XMLproperties.offsetSlidesChange)
                {
                    // The last slide change must at least be 10 seconds ago
                    if (counterTimePerSlide.Elapsed.Seconds > 10)
                    {
                        // Can be a video or a slide change
                        slideChangeDetected = true;
                        counterTimePerSlide.Stop();
                    }
                }
                else
                {
                    // a slide change was detected, than save also if a image or a video was detected
                    if (slideChangeDetected)
                    {

                        // If a video was detected save it to the DB
                        if (videoDetected)
                        {
                            // the end of the video is the slide change
                            Console.WriteLine("Video Detected" + videoDetectedTimePoint 
                                                               + " timespan "
                                                               + counterTimePerSlide.Elapsed.Minutes + ":"
                                                               + counterTimePerSlide.Elapsed.Seconds);

                            sqliteCommandVideoDetected.CommandText =
                                "INSERT INTO slides_video (capturedTime, timepoint, timespanMinute, timespanSec) VALUES ('" + startedCaptureTime + "', '"
                                                                                                                            + videoDetectedTimePoint + "', "
                                                                                                                            + counterTimePerSlide.Elapsed.Minutes + ","
                                                                                                                            + counterTimePerSlide.Elapsed.Seconds + ");";
                            sqliteCommandVideoDetected.ExecuteNonQuery();

                            //Reset the counters and indicators
                            videoDetected = false;
                            changedPixelAverage = 0;
                            changedPixelSumedUp = 0;

                            counterForSlideChangeColorPixel = 0;

                        }



                        // If a image was detected save it to the DB
                        if (imageDetected)
                        {
                            Console.WriteLine("image detected: " + imageDetectedTimePoint);
                                                        
                            sqliteCommandImageDetected.CommandText =
                                "INSERT INTO slides_picture (capturedTime, timepoint, timespanMinute, timespanSec) VALUES ('" + startedCaptureTime + "', '"
                                                                                                                            + imageDetectedTimePoint + "', "
                                                                                                                            + 0 + ","
                                                                                                                            + 0 + ");";
                            sqliteCommandImageDetected.ExecuteNonQuery();


                            imageDetected = false;
                        }





                        Console.WriteLine("Slide change detected" + slideChangeDetectedTimePoint + " timespan  "
                                                                  + counterTimePerSlide.Elapsed.Minutes + ":"
                                                                  + counterTimePerSlide.Elapsed.Seconds);

                        // Save the timepoint when a slide change is detected and how long the slide was shown
                        sqliteCommandSlideChange.CommandText =
                           "INSERT INTO slides_change (capturedTime, timepoint, timespanMinute, timespanSec) VALUES ('" + startedCaptureTime + "', '"
                                                                                                                         + slideChangeDetectedTimePoint + "', "
                                                                                                                         + counterTimePerSlide.Elapsed.Minutes + ","
                                                                                                                         + counterTimePerSlide.Elapsed.Seconds + ");";
                        sqliteCommandSlideChange.ExecuteNonQuery();

                        // Reset the counters and indicators
                        counterTimePerSlide.Reset();
                        slideChangeDetected = false;
                    }



                    // If counterTimePerSlide is not running which is the case when a slide change, or a video was detected and saved, than
                    if (!counterTimePerSlide.IsRunning)
                    {
                        counterTimePerSlide.Start();
                        // If it was a slide change than save the current time, if it was a video than nothing is done with the timepoint
                        slideChangeDetectedTimePoint = DateTime.Now;
                    }
                }

                


                // compare the frame, 30 frames ago with the current frame, problem with video detection is that from frame to frame, there are only miner changes
                // due to the compression codecs example mpeg which has the i,p frames
                if (counterForSlideChangeColorPixel >= numberOfFramesUsedForSlideChangeDetection)
                {
                    changedPixelSumedUp = 0;
                    changedPixelAverage = 0;
                    counterForSlideChangeColorPixel = 0;
                }

                if (counterForVideoDetection == 1)
                {
                   oldTotalAmountOfBGRHistogramChangedPixels = totalAmountOfBGRHistogramChangedPixels;
                }

                previousColorPixels = currentColorPixels;


                totalAmountOfBGRHistogramChangedPixels = 0;
                

                histogramBlueCurrent = new float[256];
                histogramGreenCurrent = new float[256];
                histogramRedCurrent = new float[256];
                

            }
        }
    }
}
